import { Img } from "./Img"
import { LOADED, LOADING } from "./types"
import { getParent, throttle } from "@/utils"


/**
 * 相对于浏览器视口 视口外的不加载
 */
export default class Load {
    constructor({ err, loading, preload }) {
        this.err = err
        this.loading = loading
        this.preload = preload
        this.imgArr = []
        this.loadedSet = new Set()

        this.scrollFn = this.__throttleScroll__().bind(this)
    }

    inserted(el, { value: src }) {
        this.el = el
        this.src = src
        this.imgArr.push(new Img({
            el,
            src,
            err: this.err,
            loading: this.loading,
            preload: this.preload,
            render: this.render
        }))

        this.parent = getParent(el)
        this.parent.addEventListener('scroll', this.scrollFn)

        this.onScroll() // 初始化
    }

    onScroll() {
        this.imgArr.forEach((img, i) => {
            if (img.loaded) {
                this.loadedSet.add(i)
            }
            else {
                const visible = img.visible
                // 这里是`Promise`帮你调用清理函数 所以要绑定`this`
                visible && img.load()
                    .then(this.clear.bind(this))
                    .catch(this.clear.bind(this))
            }
        })
    }

    __throttleScroll__() {
        return throttle(this.onScroll)
    }

    clear() {
        const loadArr = [...this.loadedSet]
        while (loadArr[0] !== undefined) {
            const index = loadArr.shift()
            this.loadedSet.delete(index)
            this.imgArr.splice(index, 1)
        }
    }

    render(state) {
        let src = ''
        switch (state) {
            case LOADING:
                src = this.loading
                break

            case LOADED:
                src = this.src
                break

            default:
                src = this.err
                break
        }

        this.el.setAttribute('src', src)
        return new Promise((resolve, reject) => {
            this.el.onload = resolve
            this.el.onerror = reject
        })
    }

    unbind() {
        this.parent.removeEventListener('scroll', this.scrollFn)
    }
}